package com.wiscamp.ninechapters.tags.domain.services;


import com.wiscamp.ninechapters.tags.domain.TagsErrorCodes;
import com.wiscamp.ninechapters.tags.domain.models.Entity;
import com.wiscamp.ninechapters.tags.domain.repositories.EntityRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Objects;

/**
 * Application Entity Service
 *
 * @author yifeng
 */
@Service
public class EntityService extends BaseDomainService {

    private final EntityRepository entityRepository;

    private final String CACHE_NAME = "entities";

    public EntityService(EntityRepository entityRepository) {
        this.entityRepository = entityRepository;
    }

    @Cacheable(value = CACHE_NAME, key = "#entityId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Entity getEntity(@Min(0) long entityId) {
        var entity = entityRepository.getEntity(entityId);
        if (Objects.isNull(entity)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.ENTITY_NOT_FOUND, entityId);
        }
        return entity;
    }

    @HandleDataException
    public long getEntityIdByCode(@NotBlank String entityCode, @Min(0) long appId) {
        var entity = entityRepository.getEntityByCode(entityCode, appId);
        if (Objects.isNull(entity)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.ENTITY_NOT_FOUND, null);
        }
        return entity.getEntityId();
    }

    /*@HandleServiceException
    public Entity getEntityByCode(@NotBlank String code, @Min(0) long appId) {
        return entityRepository.getEntityByCode(code, appId);
    }

    @HandleServiceException
    public Collection<Entity> getEntitiesByName(@NotBlank String name, @Min(0) long appId) {
        return entityRepository.getEntitiesByName(name, appId);
    }

    @HandleServiceException
    public Collection<Entity> getEntitiesByApplication(@Min(0) long appId) {
        return entityRepository.getEntitiesByApplication(appId);
    }*/

    @CachePut(value = CACHE_NAME, key = "#entity.entityId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Entity createEntity(@NotNull Entity entity, long operatorId) {
        var existedEntity = entityRepository.getEntityByCode(entity.getEntityCode(), entity.getApplicationId());
        if (Objects.nonNull(existedEntity)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.ENTITY_CODE_EXISTED, entity);
        }
        entity.createBy(operatorId);
        return entityRepository.saveEntity(entity);
    }

    @CachePut(value = CACHE_NAME, key = "#entity.entityId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Entity updateEntity(@NotNull Entity entity, long operatorId) {
        var existedEntity = getEntity(entity.getEntityId());
        existedEntity = entityRepository.getEntityByCode(entity.getEntityCode(), entity.getApplicationId());
        if (Objects.nonNull(existedEntity) && existedEntity.getEntityId() != entity.getEntityId()) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.ENTITY_CODE_EXISTED, entity);
        }
        entity.updateBy(operatorId);
        return entityRepository.saveEntity(entity);
    }

    @CacheEvict(value = CACHE_NAME, key = "#entityId")
    @HandleServiceException
    public boolean deleteEntity(@Min(0) long entityId, long operatorId) {
        var entity = getEntity(entityId);
        if (Objects.isNull(entity)) return false;
        // 记录操作日志
        entity.delete();
        entity.updateBy(operatorId);
        entityRepository.saveEntity(entity);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#entityId")
    @HandleServiceException
    public boolean enableEntity(@Min(0) long entityId, long operatorId) {
        var entity = getEntity(entityId);
        if (Objects.isNull(entity)) return false;
        // 记录操作日志
        entity.enable();
        entity.updateBy(operatorId);
        entityRepository.saveEntity(entity);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#entityId")
    @HandleServiceException
    public boolean disableEntity(@Min(0) long entityId, long operatorId) {
        var entity = getEntity(entityId);
        if (Objects.isNull(entity)) return false;
        // 记录操作日志
        entity.disable();
        entity.updateBy(operatorId);
        entityRepository.saveEntity(entity);
        return true;
    }



    //region entity tag
    @HandleServiceException
    public boolean bindTag(long appId, long entityId, Collection<Long> entityKeyIds, long tagId) {
        for (long entityKeyId : entityKeyIds) {
            entityRepository.bindTag(appId, entityId, entityKeyId, tagId);
        }
        return true;
    }

    @HandleServiceException
    public boolean unbindTag(long appId, long entityId, Collection<Long> entityKeyIds, long tagId) {
        for (long entityKeyId : entityKeyIds) {
            entityRepository.unbindTag(appId, entityId, entityKeyId, tagId);
        }
        return true;
    }

    /*@HandleServiceException
    public Collection<Tag> getTagsByEntity(long appId, long entityId, long entityKeyId) {
        return entityRepository.getTagsByEntity(appId, entityId, entityKeyId);
    }

    @HandleServiceException
    public Collection<Long> getEntitiesByTags(long appId, long entityId, Collection<Long> tagIds) {
        return entityRepository.getEntitiesByTags(appId, entityId, tagIds);
    }*/
    //endregion

    //region entity point
    @HandleServiceException
    public boolean bindKnowledgePoint(long appId, long entityId, long entityKeyId, long pointId, boolean isPrimary) {
        return entityRepository.bindKnowledgePoint(appId, entityId, entityKeyId, pointId, isPrimary);
    }

    @HandleServiceException
    public boolean unbindKnowledgePoint(long appId, long entityId, long entityKeyId, long pointId) {
        return entityRepository.unbindKnowledgePoint(appId, entityId, entityKeyId, pointId);
    }

    /*@HandleServiceException
    public Collection<KnowledgePoint> getKnowledgePointsByEntity(long appId, long entityId, long entityKeyId) {
        return entityRepository.getKnowledgePointsByEntity(appId, entityId, entityKeyId);
    }

    @HandleServiceException
    public Collection<KnowledgePoint> getPrimaryKnowledgePointsByEntity(long appId, long entityId, long entityKeyId, boolean isPrimary) {
        return entityRepository.getKnowledgePointsByEntity(appId, entityId, entityKeyId, isPrimary);
    }

    @HandleServiceException
    public Collection<KnowledgePoint> getKnowledgePointsByEntities(long appId, long entityId, Collection<Long> entityKeyIds) {
        return entityRepository.getKnowledgePointsByEntities(appId, entityId, entityKeyIds);
    }

    @HandleServiceException
    public Collection<Long> getEntitiesByKnowledgePoints(long appId, long entityId, Collection<Long> pointIds) {
        return entityRepository.getEntitiesByKnowledgePoints(appId, entityId, pointIds);
    }*/

    @HandleServiceException
    public long countPrimaryKeyIdsByPoint(@Min(0) long entityId, @Min(0) long pointId) {
        return entityRepository.countPrimaryKeyIdsByPoint(entityId, pointId);
    }
    //endregion
}
